home *** CD-ROM | disk | FTP | other *** search
/ MacFormat 1995 May / macformat-024.iso / Shareware City / Developers / TransSkel Pascal 2.5 / TransEdit / TransEdit.p < prev    next >
Encoding:
Text File  |  1994-12-12  |  41.9 KB  |  1,687 lines  |  [TEXT/PJMM]

  1. {    TransEdit.c version 1.0 - TransSkel plug-in module supporting an}
  2. {    arbitrary number of generic edit windows.  Each window may be}
  3. {    bound to a file.}
  4.  
  5. {    *** Requires FakeAlert.pas for proper linking! ***}
  6.  
  7. {    Shortcomings:}
  8. {        Doesn't check for the obvious out of memory conditions.}
  9.  
  10. {    TransSkel and TransEdit are public domain, and are written by:}
  11.  
  12. {            Paul DuBois}
  13. {            Wisconsin Regional Primate Research Center}
  14. {            1220 Capital Court}
  15. {            Madison WI  53706  USA}
  16.  
  17. {    UUCP:    {allegra,ihnp4,seismo}
  18. {    The Pascal Version of TransSkel is public domain and was ported by        }
  19.  
  20. {            Owen Hartnett            }
  21. {            Ωhm Software            }
  22. {            163 Richard Drive        }
  23. {            Tiverton, RI 02878        }
  24.  
  25.  
  26. {    CSNET:    omh@cs.brown.edu.CSNET                                             }
  27. {    ARPA:        omh%cs.brown.edu@relay.cs.net-relay.ARPA                        }
  28. {    UUCP:        [ihnp4,allegra]!brunix !omh                                            }
  29.  
  30.  
  31. { modified 30 December 1987 by OH for changes to version 1.03 }
  32. { modified 2   December 1988 by OH for changes for LSP 2.0 and conditional    }
  33. {                compilation.  You may now elect to allow only one edit window    }
  34. {                in your TransEdit program and save on code size.  To effect,    }
  35. {                set the conditional compilation variable singleEdit to "true."        }
  36.  
  37.  
  38. {Ingemar's notes:}
  39. {In order to get really modern, needs:}
  40. {– Apple Event handling}
  41. {– Replace hard coded strings with resources}
  42.  
  43. unit TransEdit;
  44.  
  45. interface
  46.  
  47. {$SETC singleEdit := false }
  48.     uses
  49.         FakeAlert, TransSkel;
  50.     type
  51.         SFReplyPtr = ^SFReply;
  52.  
  53.     function EWindowClose (theWind: WindowPtr): boolean;
  54.     function IsEWindow (theWind: WindowPtr): Boolean;
  55.     function IsEWindowDirty (theWind: WindowPtr): Boolean;
  56.     function GetEWindowTE (theWind: WindowPtr): TEHandle;
  57.     function GetEWindowFile (theWind: WindowPtr; fileInfo: SFReplyPtr): Boolean;
  58.     procedure SetEWindowProcs (theWind: WindowPtr; pKey, pActivate, pClose: ProcPtr);
  59.     procedure SetEWindowStyle (theWind: WindowPtr; font, size, wrap, just: integer);
  60.     procedure EWindowOverhaul (theWind: WindowPtr; showCaret, recalc, dirty: Boolean);
  61.     procedure EWindowEditOp (item: integer);
  62.     procedure SetEWindowCreator (creat: OSType);
  63.     function EWindowSave (theWind: WindowPtr): Boolean;
  64.     function EWindowSaveAs (theWind: WindowPtr): Boolean;
  65.     function EWindowSaveCopy (theWind: WindowPtr): Boolean;
  66.     function EWindowRevert (theWind: WindowPtr): Boolean;
  67.     function NewEWindow (bounds: Rect; title: Str255; visible: Boolean; behind: WindowPtr; goAway: Boolean; refNum: longint; bindToFile: Boolean): WindowPtr;
  68.     function ClobberEWindows: Boolean;
  69.     procedure TransEditInit;
  70.  
  71. implementation
  72.     const
  73.  
  74. {    Edit window types, constants, variables.}
  75.  
  76.         enter = 3;
  77.         cr = 13;
  78.         monaco = 4;
  79.         shiftKey = $200;
  80.  
  81.             { Edit menu item numbers }
  82.  
  83.         undo = 1;
  84.         cut = 3;
  85.         copy = 4;
  86.         paste = 5;
  87.         clear = 6;        { (it's ok if the host doesn't have this item) }
  88.  
  89. {    ewList points to a list of structures describing the known edit}
  90. {    windows.}
  91.  
  92. {$IFC not singleEdit }
  93.     type
  94.         EIptr = ^EditInfoRec;
  95.         EIHandle = ^EIPtr;
  96.         EditInfoRec = record
  97.                 editWind: WindowPtr;
  98.                 bound: Boolean;
  99.                 editFile: SFReply;
  100.                 editTE: TEHandle;
  101.                 dirty: Boolean;
  102.                 scroll: ControlHandle;
  103.                 visLines: integer;
  104.                 eKey, eActivate, eClose: ProcPtr;
  105.                 eNext: EIHandle;
  106.             end;
  107. {$ENDC}
  108.  
  109.     var
  110.         e_font, e_size, e_wrap, e_just: integer;
  111.         e_key, e_activate, e_close: ProcPtr;
  112.  
  113. {$IFC not singleEdit}
  114.         ewList: EIHandle;
  115.  
  116. {    Global variables - most of these are always synced to}
  117. {    the current window.  Note that not all these are set by}
  118. {    SyncGlobals, since some are not often needed.  When they}
  119. {    are all needed, use SyncAllGlobals.}
  120.  
  121.         editInfo: EIHandle;                { window's info structure      }
  122. {$ENDC}
  123.         editWind: WindowPtr;            { the window                   }
  124.         editTE: TEHandle;                { window text                  }
  125.         editScroll: ControlHandle;        { the scroll bar               }
  126.         editFile: SFReply;                { file information             }
  127.         visLines: integer;                { number of lines in window    }
  128.         bound, dirty: Boolean;            { true if window bound to file }
  129.                                         { whether window is dirty      }
  130.         eKey, eActivate, eClose: ProcPtr;            { key click notifier           }
  131.                                                     { activate event notifier      }
  132.                                                     { close notifier               }
  133.         windID: integer;
  134.         dlogWhere: Point;                { GetFile/PutFile location }
  135.         creator: OSType;                { default file creator }
  136.  
  137.         clipRgn: RgnHandle;
  138.  
  139.     procedure TransEditInit;
  140.  
  141. {Extra routine to do initialization of variables, LSP can't do this }
  142.  
  143.     begin
  144.  
  145. {    Default values for edit window text display characteristics}
  146. {    and event notification procedures}
  147.  
  148.         e_font := monaco;    { default font                 }
  149.         e_size := 9;            { default pointsize            }
  150.         e_wrap := 0;            { default word wrap (on)       }
  151.         e_just := teJustLeft;{ default justification        }
  152.         e_key := nil;        { default key procedure        }
  153.         e_activate := nil;    { default activation procedure }
  154.         e_close := nil;        { default close procedure      }
  155.  
  156. {$IFC not singleEdit}
  157.         ewList := nil;
  158. {$ENDC}
  159.         editWind := nil;
  160.         windID := 0;
  161.         dlogWhere.v := 70;
  162.         dlogWhere.h := 100;
  163.         creator := 'TEDT';
  164.     end;
  165.  
  166. { -------------------------------------------------------------------- }
  167. {                Miscellaneous Internal (private) Routines                }
  168. { -------------------------------------------------------------------- }
  169.  
  170.  
  171.  
  172. {    Save and restore the current window's clip region}
  173.  
  174.     procedure SaveClipRgn;
  175.     begin
  176.         clipRgn := NewRgn;
  177.         GetClip(clipRgn);
  178.     end;
  179.  
  180.     procedure RestoreClipRgn;
  181.     begin
  182.         SetClip(clipRgn);
  183.         DisposeRgn(clipRgn);
  184.     end;
  185.  
  186. {    Draw grow box in lower right hand corner of window.}
  187.  
  188.     procedure DrawGrowBox;
  189.  
  190.         var
  191.             r: Rect;
  192.  
  193.     begin
  194.         SaveClipRgn;
  195.         r := editWind^.portRect;
  196.         r.left := r.right - 15;
  197.         r.top := r.bottom - 15;        { draw only in corner }
  198.         ClipRect(r);
  199.         DrawGrowIcon(editWind);
  200.         RestoreClipRgn;
  201.     end;
  202.  
  203. { -------------------------------------------------------------------- }
  204. {            Lowest-level Internal (Private) Edit Window Routines        }
  205. { -------------------------------------------------------------------- }
  206. {$IFC not singleEdit}
  207. {    Get edit window info associated with window.}
  208. {    Return nil if window isn't a known edit window.}
  209.  
  210.     function GetEInfo (theWind: WindowPtr): EIHandle;
  211.  
  212.         var
  213.             h: EIHandle;
  214.             foundflag: Boolean;
  215.  
  216.     begin
  217.         h := ewList;
  218.         foundflag := false;                        { set to true when window found !}
  219.         while h <> nil do
  220.             begin
  221.                 if h^^.editWind = theWind then
  222.                     begin
  223.                         GetEInfo := h;
  224.                         h := nil;
  225.                         foundflag := true;
  226.                     end
  227.                 else
  228.                     h := h^^.eNext;
  229.             end;
  230.         if foundflag = false then
  231.             GetEInfo := nil;
  232.     end;
  233. {$ENDC}
  234.  
  235. {    Synchronize globals to an edit window and make it the}
  236. {    current port.  theWind must be a legal edit window, with one}
  237. {    exception:  if theWind is nil, the variables are synced to the}
  238. {    port that's already current.  That is safe (and correct) because:}
  239.  
  240. {    (i)     nil is only passed by edit window handler procedures,}
  241. {         which are only attached to edit windows}
  242. {    (ii) TransSkel always sets the port to the window before}
  243. {         calling the handler proc.}
  244.  
  245. {    Hence, using the current port under these circumstances always}
  246. {    produces a legal edit window.}
  247.  
  248.     procedure SyncGlobals (theWind: WindowPtr);
  249.  
  250.     begin
  251.         if theWind = nil then                    { use current window }
  252.             GetPort(theWind);
  253.         SetPort(theWind);
  254.  
  255. {$IFC not singleEdit}
  256.  
  257.         editWind := theWind;
  258.         editInfo := GetEInfo(editWind);
  259.         editTE := editInfo^^.editTE;
  260.         editScroll := editInfo^^.scroll;
  261.         visLines := editInfo^^.visLines;
  262. {$ENDC}
  263.     end;
  264.  
  265. {$IFC singleEdit}
  266.  
  267.     procedure SyncAllGlobals (theWind: Windowptr);
  268.  
  269.     begin
  270.         if theWind = nil then                    { use current window }
  271.             GetPort(theWind);
  272.         SetPort(theWind);
  273.     end;
  274.  
  275. {$ELSEC}
  276.     procedure SyncAllGlobals (theWind: WindowPtr);
  277.  
  278.     begin
  279.         SyncGlobals(theWind);                { sync display globals }
  280.         editFile := editInfo^^.editFile;
  281.         bound := editInfo^^.bound;            { procedure globals }
  282.         dirty := editInfo^^.dirty;
  283.         eKey := editInfo^^.eKey;
  284.         eActivate := editInfo^^.eActivate;
  285.         eClose := editInfo^^.eClose;
  286.     end;
  287.  
  288. {$ENDC}
  289.  
  290. {    Set dirty flag for current window}
  291.  
  292.     procedure SetDirty (boolVal: Boolean);
  293.  
  294.     begin
  295.  
  296. {$IFC singleEdit}
  297.         dirty := BoolVal;
  298. {$ELSEC}
  299.         editInfo^^.dirty := boolVal;
  300. {$ENDC}
  301.     end;
  302.  
  303. { -------------------------------------------------------------------- }
  304. {                    Internal (private) Display Routines                    }
  305. { -------------------------------------------------------------------- }
  306.  
  307. {    Calculate the dimensions of the editing rectangle for}
  308. {    editWind (which must be set properly and is assumed to be}
  309. {    the current port).  (The viewRect and destRect are the}
  310. {    same size.)  Assumes the port, text font and text size are all}
  311. {    set properly.  The viewRect is sized so that an integral}
  312. {    number of lines can be displayed in it, i.e., so that a}
  313. {    partial line never shows at the bottom.  If that's not}
  314. {    done, funny things can happen to the caret.}
  315.  
  316.     procedure GetEditRect (var r: Rect);
  317.  
  318.         var
  319.             f: FontInfo;
  320.             lineHeight: integer;
  321.  
  322.     begin
  323.         GetFontInfo(f);
  324.         lineHeight := f.ascent + f.descent + f.leading;
  325.         r := editWind^.portRect;
  326.         r.left := r.left + 4;
  327.         r.right := r.right - 17;        { leave room for scroll bar }
  328.         r.top := r.top + 2;
  329.         r.bottom := r.top + ((r.bottom - r.top - 2) div lineHeight) * lineHeight;
  330.     end;
  331.  
  332. {    Set the edit rect properly.}
  333.  
  334.     procedure SetEditRect;
  335.  
  336.         var
  337.             r: Rect;
  338.  
  339.     begin
  340.         GetEditRect(r);
  341.         editTE^^.destRect.right := r.right;
  342.         editTE^^.viewRect := r;
  343.     end;
  344.  
  345. {    Calculate the dimensions of the scroll bar rectangle for}
  346. {    editWind (which must be set properly).  Make sure that}
  347. {    the edges overlap the window frame and the grow box.}
  348.  
  349.     procedure CalcScrollRect (var r: Rect);
  350.  
  351.     begin
  352.         r := editWind^.portRect;
  353.         r.right := r.right + 1;
  354.         r.top := r.top - 1;
  355.         r.left := r.right - 16;
  356.         r.bottom := r.bottom - 14;
  357.     end;
  358.  
  359. {    Return true if the mouse is in the non-scrollbar part of the}
  360. {    edit window.}
  361.  
  362.     function PtInText (pt: Point): Boolean;
  363.  
  364.         var
  365.             r: Rect;
  366.     begin
  367.         r := editWind^.portrect;
  368.         r.right := r.right - 15;
  369.         PtInText := PtInRect(pt, r);
  370.     end;
  371.  
  372. {    Set the cursor appropriately.  If theCursor == iBeamCursor, check}
  373. {    that it's really in the text area of an edit window (and if not}
  374. {    set the cursor to an arrow instead).  Otherwise, set the cursor}
  375. {    to the given type (usually a watch).}
  376.  
  377. {    If the cursor is supposed to be set to an i-beam, it is assumed}
  378. {    that the globals are synced, because DoCursor changes them and}
  379. {    syncs them back.}
  380.  
  381. {    Pass -1 for theCursor to set the cursor to the arrow.}
  382.  
  383.     procedure DoCursor (theCursor: integer);
  384.  
  385.         var
  386.             pt: Point;
  387.             savePort: GrafPtr;
  388.             myCursor: CursHandle;
  389.  
  390.     begin
  391.         if theCursor = iBeamCursor then            { check whether there's an edit }
  392.             begin                                        { window in front and if so,    }
  393.                 theCursor := -1;                            { whether the cursor's in its   }
  394.                 if (IsEWindow(FrontWindow)) then        { text area                     }
  395.                     begin
  396.                         GetPort(savePort);
  397.                         SyncGlobals(FrontWindow);
  398.                         GetMouse(pt);
  399.                         if (PtInText(pt)) then
  400.                             theCursor := iBeamCursor;
  401.                         SyncGlobals(savePort);
  402.                     end;
  403.             end;
  404.         if theCursor = -1 then
  405. {$IFC UNDEFINED THINK_PASCAL}
  406.             SetCursor(qd.arrow)
  407. {$ELSEC}
  408.             SetCursor(arrow)
  409. {$ENDC}
  410.         else
  411.             begin
  412.                 myCursor := GetCursor(theCursor);
  413.                 SetCursor(myCursor^^);
  414.             end;
  415.     end;
  416.  
  417. {    Calculate the number of lines currently scrolled off}
  418. {    the top.}
  419.  
  420.     function LinesOffTop: integer;
  421.         var
  422.             ePtr: TEPtr;
  423.     begin
  424.         ePtr := editTE^;
  425.         LinesOffTop := ((ePtr^.viewRect.top - ePtr^.destRect.top) div ePtr^.lineHeight);
  426.     end;
  427.  
  428. {    Return the line number that the caret (or the beginning of}
  429. {    the currently selected text) is in.  Value returned is in}
  430. {    the range 0..(**editTE).nLines.  If = (**editTE).nLines, the}
  431. {    caret is past the last line.  The only special case to watch out}
  432. {{    for is when the caret is at the very end of the text.  If the}
  433. {    last character is not a carriage return, then the caret is on}
  434. {{    the (nLines-1)th line, not the (nLines)th line.}
  435. {{{    (This really should do a binary search for speed.){}
  436.  
  437.     function LineWithCaret: integer;
  438.  
  439.         var
  440.             i, nLines, teLength, selStart, lineStart: integer;
  441.             doneflag: Boolean;
  442.             mychars: CharsHandle;
  443.  
  444.     begin
  445.         selStart := editTE^^.selStart;
  446.         nLines := editTE^^.nLines;
  447.         teLength := editTE^^.teLength;
  448.  
  449.         if (selStart = teLength) then
  450.             begin
  451.                 mychars := TEGetText(editTE);
  452.                 if (teLength = 0) then
  453.                     LineWithCaret := nLines
  454.                 else if (mychars^^[teLength - 1] = char(cr)) then
  455.                     LineWithCaret := nLines
  456.                 else
  457.                     LineWithCaret := nLines - 1
  458.             end
  459.         else
  460.             begin
  461.                 i := 0;
  462.                 doneflag := false;            { Not done yet!     }
  463.                 while not doneflag do
  464.                     begin
  465.                         lineStart := editTE^^.lineStarts[i];
  466.                         if lineStart >= selStart then
  467.                             begin
  468.                                 if lineStart <> selStart then
  469.                                     i := i - 1;
  470.                                 LineWithCaret := i;
  471.                                 doneflag := true;
  472.                             end;
  473.                         i := i + 1;
  474.                     end;
  475.             end;
  476.     end;
  477.  
  478. {    Return the number of the last displayable line.  That's one}
  479. {    more than nLines if the text is empty or the last character}
  480. {    is a carriage return.}
  481.  
  482.     function LastLine: integer;
  483.  
  484.         var
  485.             nLines, teLength: integer;
  486.             mychars: CharsHandle;
  487.  
  488.     begin
  489.         nLines := editTE^^.nLines;
  490.         teLength := editTE^^.teLength;
  491.         myChars := TEGetText(editTE);
  492.         if (teLength = 0) then
  493.             nLines := nLines + 1
  494.         else if (mychars^^[teLength - 1] = char(cr)) then
  495.             nLines := nLines + 1;
  496.         LastLine := nLines;
  497.     end;
  498.  
  499. {    Set the maximum value of the scroll bar. }
  500.  
  501.     procedure SetScrollMax;
  502.  
  503.         var
  504.             topLines, scrollableLines, max: integer;
  505.  
  506.     begin
  507.         topLines := LinesOffTop;
  508.         scrollableLines := LastLine - visLines;
  509.         if topLines > scrollableLines then
  510.             max := topLines
  511.         else
  512.             max := scrollableLines;
  513.  
  514.         if max < 0 then
  515.             max := 0;
  516.  
  517.         if max <> GetCtlMax(editScroll) then
  518.             begin
  519.                 SetCtlMax(editScroll, max);
  520.                 if max > 0 then
  521.                     HiliteControl(editScroll, 0)
  522.                 else
  523.                     HiliteControl(editScroll, 255);
  524.             end;
  525.     end;
  526.  
  527. {    Set scroll bar current value (but only if it's different than}
  528. {    the current value, to avoid needless flashing).}
  529.  
  530.     procedure SetScrollValue (value: integer);
  531.  
  532.     begin
  533.         if GetCtlValue(editScroll) <> value then
  534.             SetCtlValue(editScroll, value);
  535.     end;
  536.  
  537. {    Scroll to the correct position.  lDelta is the}
  538. {    amount to CHANGE the current scroll setting by.}
  539.  
  540.     procedure ScrollText (lDelta: integer);
  541.  
  542.         var
  543.             topVisLine, newTopVisLine: integer;
  544.  
  545.     begin
  546.         topVisLine := LinesOffTop;
  547.         newTopVisLine := topVisLine + lDelta;
  548.         if newTopVisLine < 0 then                    { clip to range }
  549.             newTopVisLine := 0;
  550.         if (newTopVisline > GetCtlMax(editScroll)) then
  551.             newTopVisLine := GetCtlMax(editScroll);
  552.         SetScrollValue(newTopVisLine);
  553.         TEScroll(0, (topVisLine - newTopVisLine) * editTE^^.lineHeight, editTE);
  554.     end;
  555.  
  556. {    Scroll to home position without redrawing.{}
  557.  
  558.     procedure ScrollToHome;
  559.         var
  560.             r: Rect;
  561.  
  562.     begin
  563.         r := editTE^^.destRect;
  564.         OffsetRect(r, 0, 2 - r.top);
  565.         editTE^^.destRect := r;
  566.     end;
  567.  
  568. {    ClikLoop proc for autoscrolling text when the mouse is dragged out}
  569. {    of the text view rectangle.}
  570.  
  571. {    The clipping region has to be set to include the scroll bar,}
  572. {    because whenever this proc is called, TE has the region set down}
  573. {    to the view rectangle - if it's not reset, changes to the scroll}
  574. {    bar will not show up!}
  575.  
  576.     function AutoScroll: Boolean;
  577.  
  578.         var
  579.             p: Point;
  580.             r: Rect;
  581.  
  582.     begin
  583.         SaveClipRgn;
  584.         ClipRect(editWind^.portRect);
  585.         GetMouse(p);
  586.         r := editTE^^.viewRect;
  587.         if (p.v < r.top) then
  588.             ScrollText(-1)
  589.         else if (p.v > r.bottom) then
  590.             ScrollText(1);
  591.         RestoreClipRgn;
  592.         AutoScroll := true;
  593.     end;
  594.  
  595. {    Filter proc for tracking mousedown in scroll bar.  The code for}
  596. {    the part originally hit is shoved into the control's reference}
  597. {    value by Mouse() before this is called.}
  598.  
  599. {    I suspect odd scrolling may occur for hits in paging regions if}
  600. {    the window is allowed to size such that less than two lines show.}
  601.  
  602.     procedure TrackScroll (theScroll: ControlHandle; partCode: integer);
  603.  
  604.         var
  605.             lDelta: integer;
  606.  
  607.     begin
  608.         if partCode = GetCRefCon(theScroll) then    { still in same part? }
  609.             begin
  610.                 case partCode of
  611.                     inUpButton: 
  612.                         lDelta := -1;
  613.                     inDownButton: 
  614.                         lDelta := 1;
  615.                     inPageUp: 
  616.                         lDelta := -(visLines - 1);
  617.                     inPageDown: 
  618.                         lDelta := visLines - 1;
  619.                     otherwise
  620.                 end;
  621.                 ScrollText(lDelta);
  622.             end;
  623.     end;
  624.  
  625. {    Set the scroll bar properly and adjust the text in the}
  626. {    window so that the line containing the caret is visible.}
  627. {    If the line with the caret if more than a line outside of}
  628. {    the viewRect, try to place it in the middle of the window.}
  629. {}
  630. {    Yes, it is necessary to SetScrollMax at the end.}
  631.  
  632.     procedure AdjustDisplay;
  633.         var
  634.             caretLine, topVisLine, d: integer;
  635.     begin
  636.         SetScrollMax;
  637.         caretLine := LineWithCaret;
  638.         topVisLine := LinesOffTop;
  639.         d := caretLine - topVisLine;
  640.         if d < 0 then
  641.             if d = -1 then
  642.                 ScrollText(-1)
  643.             else
  644.                 ScrollText(d - (visLines div 2))
  645.         else
  646.             begin
  647.                 d := caretLine - (topVisLine + visLines - 1);
  648.                 if d > 0 then
  649.                     if d = 1 then
  650.                         ScrollText(1)
  651.                     else
  652.                         ScrollText(d + (visLines div 2))
  653.                 else
  654.                     SetScrollValue(topVisLine);
  655.             end;
  656.         SetScrollMax;    { might have changed from scrolling }
  657.     end;
  658.  
  659. {    Overhaul the entire display.  This is called for major}
  660. {    catastrophes, such as resizing the window, or changes to}
  661. {    the word wrap style.  It makes sure the view and}
  662. {    destination rectangles are sized properly, and that the bottom}
  663. {    line of text never scrolls up past the bottom line of the}
  664. {    window, if there's enough to fill the window, and that the}
  665. {    scroll bar max and current values are set properly.}
  666.  
  667. {    Resizing the dest rect just means resetting the right edge}
  668. {    (the top is NOT reset), since text might be scrolled off the}
  669. {    top (i.e., destRect.top != 0).}
  670.  
  671. {    Doesn't redraw the control, though!}
  672.  
  673.     procedure OverhaulDisplay (showCaret: Boolean; recalc: Boolean);
  674.  
  675.         var
  676.             r: Rect;
  677.     begin
  678.         r := editTE^^.viewRect;
  679.         EraseRect(r);                { erase current viewRect }
  680.         SetEditRect;                { recalculate editing rects }
  681.         if recalc then            { recalculate line starts }
  682.             TECalText(editTE);
  683.         visLines := (editTE^^.viewRect.bottom - editTE^^.viewRect.top) div editTE^^.lineHeight;
  684.  
  685. {$IFC not singleEdit}
  686.  
  687.         editInfo^^.visLines := visLines;
  688.  
  689. {$ENDC}
  690.  
  691.         if showCaret then
  692.             AdjustDisplay
  693.         else
  694.             SetScrollMax;
  695.         r := editTE^^.viewRect;
  696.         TEUpdate(r, editTE);
  697.     end;
  698.  
  699. { ---------------------------------------------------------------- }
  700. {                        Window Handler Routines                        }
  701. { ---------------------------------------------------------------- }
  702.  
  703.  
  704. {}
  705. {    Handle mouse clicks in window.  The viewRect is never tested}
  706. {    directly, because if it were, clicks along the top, left and}
  707. {    bottom edges of the window wouldn't register.}
  708.  
  709.     procedure Mouse (thePt: Point; t: longint; mods: integer);
  710.  
  711.         var
  712.             thePart, oldCtlValue, ignore: integer;
  713.  
  714.     begin
  715.         SyncGlobals(nil);                                { sync to current port }
  716.         thePart := TestControl(editScroll, thePt);
  717.         if thePart = inThumb then
  718.             begin
  719.                 oldCtlValue := GetCtlValue(editScroll);
  720.                 if TrackControl(editScroll, thePt, nil) = inThumb then
  721.                     ScrollText(GetCtlValue(editScroll) - oldCtlValue)
  722.             end
  723.         else if thePart <> 0 then
  724.             begin
  725.                 SetCRefCon(editScroll, longint(thePart));
  726.                 ignore := TrackControl(editScroll, thePt, @TrackScroll);
  727.             end
  728.         else if (PtInText(thePt)) then
  729.             TEClick(thePt, BitAnd(mods, shiftKey) <> 0, editTE);
  730.         SetScrollMax;
  731.     end;
  732.  
  733.     procedure callpnoarg (myProc: ProcPtr);
  734.  
  735. { For all the Procedures that are called with no arguments                            }
  736.  
  737.     inline
  738.         $205f,     {movea.l  (a7)+,a0        ; (a0) is a ptr to string, 4(a0) is mode}
  739.         $4e90;
  740.  
  741.     procedure callpBoolean (myBool: Boolean; myProc: ProcPtr);
  742.  
  743. { Two calls use Booleans as one parameter arguments.  This procedure handles    }
  744. { both of them.                                                                            }
  745.  
  746.     inline
  747.         $205f,     {movea.l  (a7)+,a0        ; (a0) is a ptr to string, 4(a0) is mode}
  748.         $4e90;
  749.  
  750. {    Handle key clicks in window}
  751.  
  752.     procedure Key (c: char; mods: integer);
  753.  
  754.     begin
  755.         SyncAllGlobals(nil);                    { sync to current port }
  756.         if c <> char(enter) then
  757.             TEKey(c, editTE);
  758.         AdjustDisplay;
  759.         SetDirty(true);
  760.         if eKey <> nil then                    { report event to the host }
  761.             callpnoarg(eKey);
  762.     end;
  763.  
  764. {    When the window comes active, highlight the scroll bar appropriately.}
  765. {    When the window is deactivated, un-highlight the scroll bar.}
  766. {    Redraw the grow box in any case.  Set the cursor (DoCursor avoids}
  767. {    changing it from an ibeam to an arrow back to an ibeam, in the case}
  768. {    where one edit window is going inactive and another is coming}
  769. {    active).}
  770. {}
  771. {    Report the event to the host.}
  772.  
  773.     procedure Activate (active: Boolean);
  774.  
  775.     begin
  776.         SyncAllGlobals(nil);        { sync to current port }
  777.         DrawGrowBox;
  778.         if active then
  779.             begin
  780.                 TEActivate(editTE);
  781.                 if GetCtlMax(editScroll) > 0 then
  782.                     HiliteControl(editScroll, 0)
  783.                 else
  784.                     HiliteControl(editScroll, 255);
  785.             end
  786.         else
  787.             begin
  788.                 TEDeactivate(editTE);
  789.                 HiliteControl(editScroll, 255);
  790.             end;
  791.         DoCursor(iBeamCursor);
  792.         if (eActivate <> nil) then    { report event to the host }
  793.             callpBoolean(active, eActivate);
  794.     end;
  795.  
  796. {    Close box was clicked.  If user specified notify proc, call it.}
  797. {    Otherwise do default close operation (ask about saving if dirty,}
  798. {    etc.).}
  799.  
  800.     procedure Close;
  801.  
  802.         var
  803.             ignore: integer;
  804.     begin
  805.         SyncAllGlobals(nil);        { sync to current port }
  806.         if eclose <> nil then
  807.             callpnoarg(eClose)
  808.         else
  809.             ignore := integer(EWindowClose(editWind));
  810.     end;
  811.  
  812. {    Update window.  The update event might be in response to a}
  813. {    window resizing.  If so, move and resize the scroll bar.}
  814. {    The ValidRect call is done because the HideControl adds the}
  815. {    control bounds box to the update region - which would generate}
  816. {    another update event!  Since everything gets redrawn below,}
  817. {    the ValidRect is used to cancel the update.}
  818.  
  819.     procedure UpDate (resized: Boolean);
  820.  
  821.         var
  822.             r: Rect;
  823.  
  824.     begin
  825.         SyncGlobals(nil);        { sync to current port }
  826.         r := editWind^.portRect;
  827.         EraseRect(r);
  828.         if resized then
  829.             begin
  830.                 HideControl(editScroll);
  831.                 r := editScroll^^.contrlRect;
  832.                 ValidRect(r);
  833.                 CalcScrollRect(r);
  834.                 SizeControl(editScroll, 16, r.bottom - r.top);
  835.                 MoveControl(editScroll, r.left, r.top);
  836.                 OverhaulDisplay(false, editTE^^.crOnly >= 0);
  837.                 ShowControl(editScroll);
  838.             end
  839.         else
  840.             begin
  841.                 OverhaulDisplay(false, false);
  842.                 DrawControls(editWind);
  843.             end;
  844.         DrawGrowBox;
  845.     end;
  846.  
  847. {    Remove the edit window from the list, and dispose of it.}
  848. {    This is called by SkelRmveWind, not directly by user program.}
  849. {}
  850. {    At this point it's too late to back out if any changes have been}
  851. {    made to the text.}
  852.  
  853. {    Since the clobber procedure is never called except for real edit}
  854. {    windows, and since the list must therefore be non-empty, it is}
  855. {    not necessary to check the legality of the window or that the}
  856. {    window's in the list.}
  857.  
  858.     procedure Clobber;
  859.  
  860. {$IFC not singleEdit}
  861.         var
  862.             h, h2: EIHandle;
  863. {$ENDC}
  864.  
  865.     begin
  866.         SyncGlobals(nil);                    { sync to current port }
  867.  
  868. {$IFC not singleEdit}
  869.         if ewList^^.editWind = editWind then    { is it the first window in list? }
  870.             begin
  871.                 h2 := ewList;
  872.                 ewList := ewList^^.eNext;
  873.             end
  874.         else
  875.             begin
  876.                 h := ewList;
  877.                 while h <> nil do
  878.                     begin
  879.                         h2 := h^^.eNext;
  880.                         if h2^^.editWind = editWind then    { found it }
  881.                             begin
  882.                                 h^^.eNext := h2^^.eNext;
  883.                                 h := nil;
  884.                             end;
  885.                         if h <> nil then
  886.                             h := h2;
  887.                     end;
  888.             end;
  889.         DisposHandle(Handle(h2));            { get rid of information structure }
  890. {$ENDC}
  891.         TEDispose(editTE);                    { toss text record }
  892.         DisposeWindow(editWind);            { disposes of scroll bar, too }
  893.         editWind := nil;
  894.         DoCursor(iBeamCursor);
  895.     end;
  896.  
  897. {    Blink the caret and make sure the cursor's an i-beam when it's}
  898. {    in the non-scrollbar part of the window.}
  899.  
  900.     procedure Idle;
  901.     begin
  902.         SyncGlobals(nil);
  903.         TEIdle(editTE);            { blink that caret! }
  904.         DoCursor(iBeamCursor);
  905.     end;
  906.  
  907. { ---------------------------------------------------------------- }
  908. {                        Internal File Routines                        }
  909. { ---------------------------------------------------------------- }
  910.  
  911.     procedure ErrMesg (s: Str255);
  912.         var
  913.             ignore: integer;
  914.     begin
  915.         ignore := FakeAlert(s, '', '', '', 1, 1, 'OK', '', '');
  916.     end;
  917.  
  918. {    Save the contents of the edit window.  If there is no file bound}
  919. {    to the window, ask for a file name.  If askForFile is true, ask}
  920. {    for a name even if the window is currently bound to a file.  If}
  921. {    bindToFile is true, bind the window to the file written to (if}
  922. {    that's different than the currently bound file), and clear the}
  923. {    window's dirty flag.}
  924.  
  925. {    Return true if the file was written without error.  Return false}
  926. {    if (a) user was asked for name and clicked Cancel (b) there was}
  927. {    some error writing the file.  In the latter case, the window is}
  928. {    not bound to any new name given by user.}
  929.  
  930. {    Always returns false if the window isn't an edit window.  This}
  931. {    simplifies EWindowSave, EWindowSaveAs, EWindowSaveCopy.  (They}
  932. {    don't do the test.)}
  933.  
  934.     function SaveFile (theWind: WindowPtr; askForFile: Boolean; bindToFile: Boolean): Boolean;
  935.  
  936.         var
  937.             f: integer;
  938.             fndrInfo: FInfo;    { finder info }
  939.             tmpFile: SFReply;
  940.             hText: Handle;
  941.             count: longint;
  942.             result, ignore: OSErr;
  943.             haveNewFile, breakflag: Boolean;
  944.  
  945.     begin
  946.         haveNewFile := false;
  947.         breakflag := false;                    { flag to detect a C 'return' statement    }
  948.         if not IsEWindow(theWind) then
  949.             begin
  950.                 SaveFile := false;
  951.                 breakflag := true;
  952.             end
  953.         else
  954.             begin
  955.                 SyncAllGlobals(theWind);
  956.                 tmpFile := editFile;
  957.                 if (bound = false) or askForFile then
  958.                     begin
  959.                         SFPutFile(dlogWhere, 'Save File as:', editFile.fName, nil, tmpFile);
  960.                         if not tmpFile.good then
  961.                             begin
  962.                                 SaveFile := false;
  963.                                 breakflag := true;
  964.                             end
  965.                         else
  966.                             begin
  967.                                 haveNewFile := true;
  968.                                 if GetFInfo(tmpFile.fName, tmpFile.vRefNum, fndrInfo) = noErr then { exists }
  969.                                     begin
  970.                                         if fndrInfo.fdType <> 'TEXT' then
  971.                                             begin
  972.                                                 ErrMesg('Not a TEXT File');
  973.                                                 SaveFile := false;
  974.                                                 breakflag := true;
  975.                                             end
  976.                                     end
  977.                                 else    { doesn't exist.  create it. }
  978.                                     begin
  979.                                         if (Create(tmpFile.fName, tmpFile.vRefNum, creator, 'TEXT') <> noErr) then
  980.                                             begin
  981.                                                 ErrMesg('Can''t Create');
  982.                                                 SaveFile := false;
  983.                                                 breakflag := true;
  984.                                             end;
  985.                                     end;
  986.                             end;
  987.                     end;
  988.             end;
  989.         if not breakflag then
  990.             begin
  991.                 if FSOpen(tmpFile.fName, tmpFile.vRefNum, f) <> noErr then
  992.                     ErrMesg('Can''t Open')
  993.                 else
  994.                     begin
  995.                         DoCursor(watchCursor);
  996.                         ignore := SetFPos(f, fsFromStart, longint(0));
  997.                         hText := editTE^^.hText;
  998.                         HLock(hText);
  999.                         count := editTE^^.teLength;
  1000.                         result := FSWrite(f, count, hText^);
  1001.                         ignore := GetFPos(f, count);
  1002.                         ignore := SetEOF(f, count);
  1003.                         ignore := FSClose(f);
  1004.                         ignore := FlushVol(nil, tmpFile.vRefNum);
  1005.                         HUnlock(hText);
  1006.                         DoCursor(iBeamCursor);
  1007.                         if result = noerr then
  1008.                             begin
  1009.                                 if bindToFile then
  1010.                                     begin
  1011.                                         SetDirty(false);
  1012.                                         if haveNewFile then
  1013.                                             begin
  1014.                                                 SetWTitle(editWind, tmpFile.fName);
  1015.  
  1016. {$IFC singleEdit}
  1017.                                                 bound := true;
  1018.                                                 editFile := tmpFile;
  1019. {$ELSEC}
  1020.                                                 editInfo^^.bound := true;
  1021.                                                 editInfo^^.editFile := tmpFile;
  1022. {$ENDC}
  1023.                                             end;
  1024.                                     end;
  1025.                                 SaveFile := true;
  1026.                                 breakflag := true;
  1027.                             end
  1028.                         else
  1029.                             ErrMesg('Write error!');
  1030.                     end;
  1031.                 if not breakflag then
  1032.                     SaveFile := false;
  1033.             end;
  1034.     end;
  1035.  
  1036. {    Revert to version of file saved on disk.  Doesn't check whether}
  1037. {    the window's really bound to a file or not, doesn't ask whether}
  1038. {    to really revert if the window's dirty, does no redrawing, etc.}
  1039. {    Just reports whether the file was read in successfully.}
  1040.  
  1041.     function Revert: Boolean;
  1042.  
  1043.         var
  1044.             result: Boolean;
  1045.             f: integer;
  1046.             len: longint;
  1047.             h: Handle;
  1048.             ignore: OSErr;
  1049.  
  1050.     begin
  1051.         result := false;
  1052.         DoCursor(watchCursor);
  1053.         if FSOpen(editFile.fName, editFile.vRefNum, f) <> noErr then
  1054.             ErrMesg('Couldn''t open file')
  1055.         else
  1056.             begin
  1057.                 ignore := GetEOF(f, len);
  1058.                 if len >= 32000 then
  1059.                     ErrMesg('File is too big')
  1060.                 else
  1061.                     begin
  1062.                         h := Handle(TEGetText(editTE));
  1063.                         SetHandleSize(h, len);
  1064.                         HLock(h);
  1065.                         ignore := FSRead(f, len, h^);
  1066.                         HUnlock(h);
  1067.                         editTE^^.teLength := len;
  1068.                         TESetSelect(longint(0), longint(0), editTE);    { set caret at start }
  1069.                         result := true;
  1070.                         SetDirty(false);
  1071.                     end;
  1072.                 ignore := FSClose(f);
  1073.             end;
  1074.         DoCursor(iBeamCursor);
  1075.         Revert := result;
  1076.     end;
  1077.  
  1078. { ------------------------------------------------------------ }
  1079. {            Lowest-level Interface (Public) Routines            }
  1080. { ------------------------------------------------------------ }
  1081.  
  1082.  
  1083. {}
  1084. {    Return true/false to indicate whether the window is really an}
  1085. {    edit window.}
  1086.  
  1087.     function IsEWindow;
  1088.     begin
  1089.  
  1090. {$IFC singleEdit}
  1091.         ISEWindow := (theWind = editWind) & (editWind <> nil);
  1092. {$ELSEC}
  1093.         IsEWindow := GetEInfo(theWind) <> nil;
  1094. {$ENDC}
  1095.     end;
  1096.  
  1097. {    Return true/false to indicate whether the text associated with}
  1098. {    the window has been changed since the last save/revert (or since}
  1099. {    created, if not bound to file).}
  1100.  
  1101.     function IsEWindowDirty;
  1102.  
  1103. {$IFC not singleEdit}
  1104.         var
  1105.             eInfo: EIHandle;
  1106. {$ENDC}
  1107.  
  1108.     begin
  1109. {$IFC not singleEdit}
  1110.         eInfo := GetEInfo(theWind);
  1111.         if eInfo <> nil then
  1112.             IsEWindowDirty := eInfo^^.dirty
  1113.         else
  1114.             IsEwindowDirty := false;
  1115. {$ELSEC}
  1116.         if (IsEWindow(theWind)) then
  1117.             IsEWindowDirty := dirty
  1118.         else
  1119.             IsEWindowDirty := false;
  1120. {$ENDC}
  1121.     end;
  1122.  
  1123. {    Return a handle to the TextEdit record associated with the edit}
  1124. {    window, or nil if it's not an edit window}
  1125.  
  1126.     function GetEWindowTE;
  1127.  
  1128. {$IFC not singleEdit}
  1129.         var
  1130.             eInfo: EIHandle;
  1131. {$ENDC}
  1132.  
  1133.     begin
  1134.  
  1135. {$IFC not singleEdit}
  1136.         eInfo := GetEInfo(theWind);
  1137.         if eInfo <> nil then
  1138.             GetEWindowTE := eInfo^^.editTE
  1139.         else
  1140.             GetEWindowTE := nil;
  1141. {$ELSEC}
  1142.         if IsEWindow(theWind) then
  1143.             GetEWindowTE := editTE
  1144.         else
  1145.             GetEWindowTE := nil;
  1146. {$ENDC}
  1147.     end;
  1148.  
  1149. {    Return true/false depending on whether the editor is bound to}
  1150. {    a file or not, and a copy of the file info in the second}
  1151. {    argument.  Pass nil for fileInfo if only want the return status.}
  1152. {    Returns false if it's not an edit window.}
  1153.  
  1154.     function GetEWindowFile;
  1155.  
  1156. {$IFC not singleEdit}
  1157.         var
  1158.             eInfo: EIHandle;
  1159. {$ENDC}
  1160.  
  1161.     begin
  1162. {$IFC not singleEdit}
  1163.         eInfo := GetEInfo(theWind);
  1164.         if eInfo <> nil then
  1165.             begin
  1166.                 if fileInfo <> nil then
  1167.                     fileInfo^ := eInfo^^.editFile;
  1168.                 GetEWindowFile := eInfo^^.bound
  1169.             end
  1170.         else
  1171.             GetEWindowFile := false;
  1172. {$ELSEC}
  1173.         if IsEWindow(theWind) then
  1174.             begin
  1175.                 if fileInfo <> nil then
  1176.                     fileInfo^ := editFile;
  1177.                 GetEWindowFile := bound;
  1178.             end
  1179.         else
  1180.             GetEWindowFile := false;
  1181. {$ENDC}
  1182.     end;
  1183.  
  1184. { ---------------------------------------------------------------- }
  1185. {                    Interface Display Routines                        }
  1186. { ---------------------------------------------------------------- }
  1187.  
  1188.  
  1189. {}
  1190. {    Install event notification procedures for an edit window.}
  1191.  
  1192.     procedure SetEWindowProcs;
  1193.  
  1194.  
  1195. {$IFC not singleEdit}
  1196.         var
  1197.             eInfo: EIHandle;
  1198. {$ENDC}
  1199.  
  1200.     begin
  1201.         if theWind = nil then            { reset window creation defaults }
  1202.             begin
  1203.                 e_key := pKey;
  1204.                 e_activate := pActivate;
  1205.                 e_close := pClose;
  1206.             end
  1207.         else
  1208. {$IFC not singleEdit}
  1209.             begin
  1210.                 eInfo := GetEInfo(theWind);
  1211.                 if eInfo <> nil then
  1212.                     begin
  1213.                         eInfo^^.eKey := pKey;
  1214.                         eInfo^^.eActivate := pActivate;
  1215.                         eInfo^^.eClose := pClose;
  1216.                     end;
  1217.             end;
  1218. {$ELSEC}
  1219.         begin
  1220.             if IsEWindow(theWind) then
  1221.                 begin
  1222.                     eKey := pKey;
  1223.                     eActivate := pActivate;
  1224.                     eClose := pClose;
  1225.                 end;
  1226.         end;
  1227. {$ENDC}
  1228.     end;
  1229.  
  1230. {    Change the text display characteristics of an edit window}
  1231. {    and redisplay it.}
  1232.  
  1233. {    Scroll to home position before overhauling, because although}
  1234. {    the overhaul sets the viewRect to display an integral number}
  1235. {    of lines, there's no guarantee that the destRect offset will}
  1236. {    also be integral except at home position.  Clipping is set to}
  1237. {    an empty rect so the scroll doesn't show.}
  1238.  
  1239.     procedure SetEWindowStyle;
  1240.         var
  1241.             savePort: GrafPtr;
  1242.             f: FontInfo;
  1243.             te: TEHandle;
  1244.             r: Rect;
  1245.             oldWrap: integer;
  1246.  
  1247.     begin
  1248.         if theWind = nil then            { reset window creation defaults }
  1249.             begin
  1250.                 e_font := font;
  1251.                 e_size := size;
  1252.                 e_wrap := wrap;
  1253.                 e_just := just;
  1254.             end
  1255.         else if IsEWindow(theWind) then
  1256.             begin
  1257.                 GetPort(savePort);
  1258.                 SyncGlobals(theWind);    { sync and set port }
  1259.                 te := editTE;
  1260.                 ScrollToHome;
  1261.  
  1262.                 oldWrap := te^^.crOnly;
  1263.                 te^^.crOnly := wrap;
  1264.                 TESetJust(just, te);    { set justification }
  1265.  
  1266.                 TextFont(font);         { set the font and point size }
  1267.                 TextSize(size);        { of text record }
  1268.                 GetFontInfo(f);
  1269.                 te^^.lineHeight := f.ascent + f.descent + f.leading;
  1270.                 te^^.fontAscent := f.ascent;
  1271.                 te^^.txFont := font;
  1272.                 te^^.txSize := size;
  1273.  
  1274.                 OverhaulDisplay(true, (oldWrap >= 0) or (wrap >= 0));
  1275.  
  1276.                 SetPort(savePort);
  1277.             end;
  1278.     end;
  1279.  
  1280. {    Redo display.  Does not save current port.  This is used by hosts}
  1281. {    that mess with the text externally to TransEdit.  The arguments}
  1282. {    determine whether the text is scrolled to show the line with the}
  1283. {    caret, whether the lineStarts are recalculated, and whether the}
  1284. {    text should be marked dirty or not.}
  1285.  
  1286.     procedure EWindowOverhaul;
  1287.     begin
  1288.         if (IsEWindow(theWind)) then
  1289.             begin
  1290.                 SyncGlobals(theWind);
  1291.                 OverhaulDisplay(showCaret, recalc);
  1292.                 DrawControls(editWind);
  1293.                 SetDirty(dirty);
  1294.             end;
  1295.     end;
  1296.  
  1297. { ---------------------------------------------------------------- }
  1298. {                        Menu Interface Routine                        }
  1299. { ---------------------------------------------------------------- }
  1300.  
  1301.  
  1302. {}
  1303. {    Do Edit menu selection.  This is only valid if an edit}
  1304. {    window is frontmost.}
  1305.  
  1306.     procedure EWindowEditOp;
  1307.  
  1308.         var
  1309.             ignore: integer;
  1310.     begin
  1311.         if IsEWindow(FrontWindow) then
  1312.             begin
  1313.                 SyncGlobals(FrontWindow);
  1314.                 case item of
  1315.  
  1316. {    cut selection, put in TE Scrap, clear clipboard and put}
  1317. {    TE scrap in it}
  1318.  
  1319.                     cut: 
  1320.                         begin
  1321.                             TECut(editTE);
  1322.                             ignore := ZeroScrap;
  1323.                             ignore := TEToScrap;
  1324.                         end;
  1325.  
  1326. {    copy selection to TE Scrap, clear clipboard and put}
  1327. {    TE scrap in it}
  1328.  
  1329.                     copy: 
  1330.                         begin
  1331.                             TECopy(editTE);
  1332.                             ignore := ZeroScrap;
  1333.                             ignore := TEToScrap;
  1334.                         end;
  1335.  
  1336. {    get clipboard into TE scrap, put TE scrap into edit record}
  1337.  
  1338.                     paste: 
  1339.                         begin
  1340.                             ignore := TEFromScrap;
  1341.                             TEPaste(editTE);
  1342.                         end;
  1343.  
  1344. {    delete selection without putting into TE scrap or clipboard}
  1345.  
  1346.                     clear: 
  1347.                         TEDelete(editTE);
  1348.                     otherwise
  1349.                 end;
  1350.                 AdjustDisplay;
  1351.                 SetDirty(true);
  1352.             end;
  1353.     end;
  1354.  
  1355. { ---------------------------------------------------------------- }
  1356. {                        Interface File Routines                        }
  1357. { ---------------------------------------------------------------- }
  1358.  
  1359.  
  1360. {}
  1361. {    Set file creator for any files created by TransEdit}
  1362.  
  1363.     procedure SetEWindowCreator;
  1364.     begin
  1365.         creator := creat;
  1366.     end;
  1367.  
  1368. {    Save the contents of the given window}
  1369.  
  1370.     function EWindowSave;
  1371.     begin
  1372.         EWindowSave := SaveFile(theWind, false, true);    { window to save }
  1373.     { don't ask for file if have one }
  1374.     { bind to new file if one given }
  1375.  
  1376.     end;
  1377.  
  1378. {    Save the contents of the given window under a new name}
  1379. {    and bind to that name.}
  1380.  
  1381.     function EWindowSaveAs;
  1382.     begin
  1383.         EWindowSaveAs := SaveFile(theWind, true, true);{ window to save }
  1384.     { ask for file even if have one }
  1385.     { bind to new file if one given }
  1386.     end;
  1387.  
  1388. {    Save the contents of the given window under a new name, but}
  1389. {    don't bind to the name.}
  1390.  
  1391.     function EWindowSaveCopy;
  1392.     begin
  1393.         EWindowSaveCopy := SaveFile(theWind, true, false);    { window to save }
  1394.     { ask for file even if have one }
  1395.     { don't bind to file }
  1396.  
  1397.     end;
  1398.  
  1399. {    Close the window.  If it's dirty and is either bound to a file}
  1400. {    or (if not bound) has some text in it, ask about saving it first,}
  1401. {    giving user option of saving changes, tossing them, or}
  1402. {    cancelling altogether.}
  1403.  
  1404. {    Return true if the file was saved and the window closed, false if}
  1405. {    user cancelled or there was an error.}
  1406.  
  1407.     function EWindowClose;
  1408.         var
  1409.             return: Boolean;
  1410.     begin
  1411.         return := true;
  1412.         if IsEWindow(theWind) = true then
  1413.             begin
  1414.                 SyncAllGlobals(theWind);
  1415.                 if ((bound or (editTE^^.teLength > 0)) and dirty) then
  1416.                     case (FakeAlert('Save changes to"', editFile.fName, '"?', '', 3, 3, 'Cancel', 'Discard', 'Save')) of
  1417.                         1:         { cancel Close }
  1418.                             return := false;
  1419.                         2: 
  1420.                             ;             { toss changes }
  1421.                         3: 
  1422.                             if SaveFile(editWind, false, false) = false then        { window to save }
  1423.     { don't ask for name }
  1424.     { don't bind to name }
  1425.  
  1426.                                 return := false;    { cancelled or error - cancel Close }
  1427.                         otherwise
  1428.                     end;
  1429.                 if return then
  1430.                     SkelRmveWind(editWind);
  1431.                 EWindowClose := return;
  1432.             end;
  1433.     end;
  1434.  
  1435. {    Revert to saved version of file on disk.  theWind must be an edit}
  1436. {    window, and must be bound to a file.  Returns false if one of these}
  1437. {    conditions is not met, or if they are met but there was an error}
  1438. {    reading the file.}
  1439.  
  1440. {    The window need not be dirty, but if it is, the user is asked}
  1441. {    whether to really revert.}
  1442.  
  1443.     function EWindowRevert;
  1444.         var
  1445.             return: Boolean;
  1446.     begin
  1447.         return := true;
  1448.         if not IsEWindow(theWind) then
  1449.             return := false
  1450.         else
  1451.             begin
  1452.                 SyncAllGlobals(theWind);
  1453.                 if not bound then        { no file to revert to }
  1454.                     return := false
  1455.                 else
  1456.                     begin
  1457.                         if dirty then
  1458.                             if FakeAlert('"', editFile.fName, '" has been changed.  Really revert?', '', 2, 1, 'Cancel', 'Revert', '') = 1 then
  1459.                                 return := false;
  1460.                     end;
  1461.             end;
  1462.         if return = true then
  1463.             if Revert = false then
  1464.                 return := false;
  1465.         if return = true then
  1466.             begin
  1467.                 ScrollToHome;
  1468.                 OverhaulDisplay(true, true);
  1469.                 DrawControls(editWind);
  1470.                 ValidRect(editWind^.portRect);
  1471.             end;
  1472.         EWindowRevert := return;
  1473.     end;
  1474.  
  1475. { ---------------------------------------------------------------- }
  1476. {            Interface Initialization/Termination Routines            }
  1477. { ---------------------------------------------------------------- }
  1478.  
  1479.  
  1480. {}
  1481. {    Initialize the window and associated data structures.}
  1482. {    Return window pointer or nil if some sort of error.}
  1483. {}
  1484. {    Preserves the current port.}
  1485.  
  1486.     function NewEWindow;
  1487.  
  1488.         var
  1489.             savePort: GrafPtr;
  1490.             r: Rect;
  1491.             mytype: SFTypeList;
  1492.             s, s2: Str255;
  1493.             tPtr: string[64];
  1494.  
  1495. {$IFC not singleEdit}
  1496.             eInfo: EIHandle;
  1497. {$ENDC}
  1498.  
  1499.             failure: Boolean;
  1500.             dummy: Boolean;
  1501.  
  1502.     begin
  1503. {$IFC singleEdit}
  1504.         if editWind <> nil then
  1505.             begin
  1506.                 NewEWindow := nil;
  1507.                 exit(NewEWindow);
  1508.             end;
  1509. {$ENDC}
  1510.  
  1511.         mytype[0] := 'TEXT';
  1512.         failure := false;            {no failure yet!}
  1513.         if bindToFile then
  1514.  
  1515. {    If supposed to bind to file, ask for name.  Return without doing}
  1516. {    anything if Cancel button clicked.}
  1517.  
  1518.             begin
  1519.                 SFGetFile(dlogWhere, '', nil, 1, myType, nil, editFile);
  1520.                 if not editFile.good then
  1521.                     failure := true
  1522.             end;
  1523.         if not failure then
  1524.             begin
  1525.                 bound := bindToFile;
  1526.                 if bound then
  1527.  
  1528. {    Create window and install handler.  Set window title:  If window is}
  1529. {    to be bound to file, use name of file.  Otherwise use any title that}
  1530. {    was passed in.  If nil was passed, use a default name ("Untitled nnn").}
  1531. {    Also copy the name into the file info structure even if the window is}
  1532. {    unbound, because the Save operations expect to find it there as the}
  1533. {    most likely name to use if the window is untitled.}
  1534.  
  1535. {    Save and restore port, because it gets reset by the rest of the}
  1536. {    initialization code.}
  1537.  
  1538.                     tPtr := editFile.fName
  1539.                 else
  1540.                     begin
  1541.                         if title <> '' then
  1542.                             tPtr := title
  1543.                         else
  1544.                             begin
  1545. {$IFC not singleEdit}
  1546.                                 windId := windID + 1;                    { Who's says C is easier?  The C code for this }
  1547.                                 NumToString(longint(windID), s2);        { is ridiculous!!!!!                                    }
  1548.                                 tPtr := concat('Untitled ', s2);
  1549. {$ELSEC}
  1550.                                 tPtr := 'Untitled';
  1551. {$ENDC}
  1552.                             end;
  1553.                         editFile.fName := tPtr;
  1554.                     end;
  1555.                 editWind := NewWindow(nil, bounds, tPtr, false, documentProc, behind, goAway, refNum);
  1556.  
  1557.                 GetPort(savePort);
  1558.                 dummy := SkelWindow(editWind, @Mouse, @Key, @Update, @Activate, @Close, @Clobber, @Idle, true);
  1559.  
  1560.         { mouse click handler }
  1561.         { key click handler }
  1562.         { window updating procedure }
  1563.         { window activate/deactivate procedure }
  1564.         { window close procedure }
  1565.         { window disposal procedure }
  1566.         { idle proc }
  1567.         { idle only when frontmost }
  1568.  
  1569. {    Build the scroll bar.}
  1570.  
  1571.                 CalcScrollRect(r);
  1572.  
  1573.                 editScroll := NewControl(editWind, r, '', true, 0, 0, 0, scrollBarProc, longint(0));
  1574.  
  1575. {    Create the TE record used for text display.  Use default}
  1576. {    characteristics.}
  1577.  
  1578.                 GetEditRect(r);
  1579.                 editTE := TENew(r, r);
  1580.                 SetClikLoop(@AutoScroll, editTE);            { set autoscroll proc }
  1581.  
  1582. {$IFC not singleEdit}
  1583. {    Get new information structure, attach to list of known edit}
  1584. {    windows.}
  1585.  
  1586.                 eInfo := EIHandle(NewHandle(Size(sizeof(EditInfoRec))));
  1587.                 editInfo := eInfo;
  1588.                 eInfo^^.eNext := ewList;
  1589.                 ewList := eInfo;
  1590.                 eInfo^^.editWind := editWind;
  1591.                 eInfo^^.scroll := editScroll;
  1592.                 eInfo^^.editTE := editTE;
  1593.                 eInfo^^.bound := bound;
  1594.                 eInfo^^.editFile := editFile;
  1595. {$ENDC}
  1596.  
  1597.  
  1598. {    Install default event notification procedures, font characteristics.}
  1599.  
  1600.                 SetEWindowProcs(editWind, e_key, e_activate, e_close);
  1601.                 SetEWindowStyle(editWind, e_font, e_size, e_wrap, e_just);
  1602.                 SetDirty(false);
  1603.  
  1604. {    If supposed to read file, do so.  Check the return value of}
  1605. {    Revert and toss the window if there was an error.}
  1606.  
  1607.                 if bindToFile then
  1608.                     if (Revert = false) then
  1609.                         begin
  1610.                             SkelRmveWind(editWind);
  1611.                             SetPort(savePort);
  1612.                             failure := true;
  1613.                         end;
  1614.             end;
  1615.         if not failure then
  1616.             begin
  1617.  
  1618. {    Show window if specified as visible, and return a pointer to it.}
  1619.  
  1620.                 SyncGlobals(editWind);
  1621.                 OverhaulDisplay(true, true);
  1622.                 if visible then
  1623.                     ShowWindow(editWind);
  1624.                 SetPort(savePort);
  1625.                 NewEWindow := editWind;
  1626.             end
  1627.         else
  1628.             NewEWindow := nil;
  1629.     end;
  1630.  
  1631. {    Look through the list of windows, shutting down all the edit}
  1632. {    windows.  If any window is dirty, ask user about saving it first.}
  1633. {    If the user cancels on any such request, ClobberEWindows returns}
  1634. {    false.  If all edit windows are shut down, return true.  It is}
  1635. {    then safe for the host to exit.}
  1636.  
  1637. {    When a window *is* shut down, have to start looking through the}
  1638. {    window list again, since theWind no longer points anywhere}
  1639. {    meaningful.}
  1640.  
  1641.     function ClobberEWindows;
  1642.  
  1643.         var
  1644.             theWind: WindowPtr;
  1645.             breakflag, flag2: Boolean;
  1646.             mypeek: WindowPeek;
  1647.  
  1648.     begin
  1649.         breakflag := false;
  1650.         while not breakflag do
  1651.             begin
  1652.                 theWind := FrontWindow;
  1653.                 flag2 := false;
  1654.                 while (theWind <> nil) and not flag2 do        { all edit windows are not shut down }
  1655.                     begin
  1656.                         if ISEWindow(theWind) then
  1657.                             flag2 := true
  1658.                         else
  1659.                             begin
  1660.                                 mypeek := WindowPeek(theWind);
  1661.                                 theWind := WindowPtr(mypeek^.nextWindow);
  1662.                             end;
  1663.                     end;
  1664.                 if theWind = nil then
  1665.                     begin
  1666.                         ClobberEWindows := true;
  1667.                         breakflag := true;
  1668.                     end
  1669.                 else
  1670.                     begin
  1671.                         if theWind <> FrontWindow then
  1672.                             begin
  1673.                                 SelectWindow(theWind);
  1674.                                 ShowWindow(theWind);
  1675.                                 EWindowOverhaul(theWind, false, false, IsEWindowDirty(theWind));
  1676.                                 SetPort(theWind);
  1677.                                 ValidRect(theWind^.portRect);
  1678.                             end;
  1679.                         if EWindowClose(theWind) = false then        { cancel or error }
  1680.                             begin
  1681.                                 ClobberEWindows := false;
  1682.                                 breakflag := true;
  1683.                             end;
  1684.                     end;
  1685.             end;
  1686.     end;
  1687. end.